<?php

/**
 * CCoreNetPop3
 * CCoreNetPop3Config
 *
 * @author Usenko Timur <usenko.timur@gmail.com>
 * @package net
 * @subpackage pop3
 */

CCore::Using('net.abstract');
CCore::Using('net.config');

/**
 * @package net
 * @subpackage pop3
 */
class CCoreNetPop3 extends ACoreNetAbstract
{
	#<editor-fold defaultstate="collapsed" desc="private">
	/**
	 * @var string
	 */
	private $_sApopBanner;
	#</editor-fold>

	/**
	 * @param CCoreNetPop3Config $oPop3Config
	 * @return CCoreNetPop3
	 */
	public function __construct(CCoreNetPop3Config $oPop3Config)
	{
		CCore::CheckTypes(array('$oPop3Config'));

		parent::__construct($oPop3Config);

		$this->oFollowers->InitPrefixs('POP3', 'POP3[Error]', 'POP3[Warning]', 'POP3[Notice]');
		$this->oFollowers->SetSecretValues(array(
			$oPop3Config->GetValue('Host'), $oPop3Config->GetValue('Login'), $oPop3Config->GetValue('Password')
		));
		
		$this->_sApopBanner = '';
	}

	/**
	 * @return bool
	 */
	public function Connect()
	{
		$iPort = $this->oConfig->GetValue('Port', 'int');
		return parent::Connect(995 === $iPort) && $this->_isSuccess() && $this->_parseApopBanner($this->GetLastBuffer());
	}

	/**
	 * @return bool
	 */
	public function Login()
	{
		$sLogin = $this->oConfig->GetValue('Login', 'string');
		$sPassword = $this->oConfig->GetValue('Password', 'string');

		if (0 < strlen($this->_sApopBanner))
		{
			return $this->_executeCommand('APOP '.$sLogin.' '.md5($this->_sApopBanner.$sPassword));
		}
		else
		{
			return $this->_executeCommand('USER '.$sLogin) && $this->_executeCommand('PASS '.$sPassword);
		}
	}

	/**
	 * @return bool
	 */
	public function ConnectAndLogin()
	{
		return $this->Connect() && $this->Login();
	}

	/**
	 * @return bool
	 */
	public function Logout()
	{
		return $this->_executeCommand('QUIT');
	}

	/**
	 * @return bool
	 */
	public function LogoutAndDisconnect()
	{
		return $this->Logout() && $this->Disconnect();
	}

	/**
	 * @return bool
	 */
	public function Noop()
	{
		return $this->_executeCommand('NOOP');
	}

	/**
	 * @return bool
	 */
	public function Rset()
	{
		return $this->_executeCommand('RSET');
	}

	/**
	 * @return bool
	 */
	public function Stat(&$iCount, &$iSize)
	{
		if (!$this->_executeCommand('STAT'))
		{
			return false;
		}

		$aMatch = array();
		if (preg_match('/\+OK ([\d]+) ([\d]+)/', $this->GetLastBuffer(), $aMatch)
				&& isset($aMatch[1], $aMatch[2]))
		{
			$iCount = (int) $aMatch[1];
			$iSize = (int) $aMatch[2];
			return true;
		}
		return false;
	}

	/**
	 * @param int $iIndex
	 * @return bool
	 */
	public function DeleteMessage($iIndex)
	{
		CCore::CheckTypes(array('$iIndex'));

		return $this->_executeCommand('DELE '.$iIndex);
	}

	/**
	 * @param int $niIndex = null
	 * @return array | bool
	 */
	public function Uidl($niIndex = null)
	{
		CCore::CheckTypes(array('$niIndex'));
		
		return $this->_getIndexLines('UIDL', $niIndex, 'string');
	}

	/**
	 * @param int $niIndex = null
	 * @return array | bool
	 */
	public function Sizes($niIndex = null)
	{
		CCore::CheckTypes(array('$niIndex'));

		return $this->_getIndexLines('LIST', $niIndex, 'integer');
	}

	/**
	 * @param string $sCommand
	 * @param int $niIndex = null
	 * @return array | bool
	 */
	private function _getIndexLines($sCommand, $niIndex = null, $sValueType = 'string')
	{
		CCore::CheckTypes(array('$sCommand', '$niIndex', '$sValueType'));

		if (null !== $niIndex && $niIndex > 0)
		{
			if ($this->_executeCommand($sCommand.' '.$niIndex))
			{
				$aMatch = array();
				if (preg_match('/^\+OK ([\d]+) (.+)/', $this->GetLastBuffer(), $aMatch)
						&& isset($aMatch[1], $aMatch[2]))
				{
					$aMatch[2] = trim($aMatch[2]);
					if (settype($aMatch[2], $sValueType))
					{
						return array((int) $aMatch[1] => $aMatch[2]);
					}
				}
			}
		}
		else
		{
			if ($this->_executeCommand($sCommand))
			{
				$aReturn = array();
				while (true)
				{
					$mLine = $this->getLine();
					if (is_string($mLine))
					{
						if ('.' !== trim($mLine))
						{
							$aMatch = array();
							if (preg_match('/^([\d]+) (.+)/', $mLine, $aMatch)
									&& isset($aMatch[1], $aMatch[2]))
							{
								$aMatch[2] = trim($aMatch[2]);
								if (settype($aMatch[2], $sValueType))
								{
									$aReturn[(int) $aMatch[1]] = $aMatch[2];
								}
							}
							unset($aMatch);
						}
						else
						{
							break;
						}
					}
					else
					{
						return false;
					}
				}
				return $aReturn;
			}
		}

		return false;
	}
	
	/**
	 * @param string $sCommand
	 * @return bool
	 */
	private function _executeCommand($sCommand)
	{
		CCore::CheckTypes(array('$sCommand'));

		if (!$this->IsConnected())
		{
			return false;
		}

		return $this->putLine($sCommand) && $this->_isSuccess();
	}

	/**
	 * @return bool
	 */
	private function _isSuccess()
	{
		$bResult = false;

		$sLine = $this->getLine();
		if (false === $sLine)
		{
			$this->Disconnect();
		}
		else
		{
			if ('+OK' === substr($sLine, 0, 3))
			{
				$bResult = true;
			}
			else
			{
				$bResult = false;
				$this->oFollowers->Write('response not success ('.
					CCoreBaseUtils::ShowCRLF($sLine).').', CCoreBaseFollowersCollection::WARNING);

				$this->Disconnect();
			}
		}

		return $bResult;
	}

	/**
	 * @param string $sLoginText
	 * @return bool
	 */
	private function _parseApopBanner($sLoginText)
	{
		CCore::CheckTypes(array('$sLoginText'));

		$aMatches = array();
		if (preg_match('/<.+>/', $sLoginText, $aMatches) && isset($aMatches[0]) && 0 < strlen($aMatches[0]))
		{
			$this->_sApopBanner = $aMatches[0];
		}
		
		return true;
	}
}

/**
 * @package net
 * @subpackage pop3
 */
class CCoreNetPop3Config extends ACoreNetConfigWithLoginAndPassword
{
}
